#include "Object3D.h"

Object3D::Object3D ()
{
	state = NORMAL;
	_color[0] = _color[1] = _color[2] = _color[3] = 1.0;
	for (int i = 0; i < 3; i++)
		{
			_rotation[i] = 0.0;
			_translation[i] = 0.0;
		}
}

Object3D::Object3D (Mesh *m) : Mesh (*m) 
{
	compute_com ();
	state = NORMAL;
	_color[0] = _color[1] = _color[2] = _color[3] = 1.0;
}

Object3D::Object3D (Object3D &obj)
{
	state = obj.get_state ();
	switch (state)
	{
		case NORMAL :
			_color[0] = _color[1] = _color[2] = 1.0;
			break;
		case SELECTED :
			_color[0] = 1.0;
			_color[1] = _color[2] = 0.0;
			break;
		case EDITED :
			_color[0] = _color[1] = 0.0;
			_color[2] = 1.0;
			break;
	}
	_color[3]=1.0;
	verts = vector<Vertex3D *> (obj.get_verts ());
	triangs = vector<Triangle *> (obj.get_triangs ());
	for (int i = 0; i < 3; i++)
		{
			_rotation[i] = 0.0;
			_translation[i] = 0.0;
		}
	compute_com ();
}

Object3D::Object3D
(const char *path)
{
  
  state = NORMAL;
  for (int i = 0; i < 3; i++)
    {
      _rotation[i] = 0.0;
      _translation[i] = 0.0;
    }
  int line = 0;
  char staux[800];
  float a=0, b=0, c=0;
  GLdouble aux[3];
  float vx=0, vy=0, vz=0;
  FILE *f;
  setlocale(LC_NUMERIC, "C");
  vector<Vertex3D *> vertices;
  vector<Triangle *> triangs;	
  
  if(!(f = fopen (path, "r")))
    {
      cerr << "File: " << path << " not found!" << endl;
      //exit(0);
    }
  
  while (fgets(staux, 800, f)!=NULL){

    line++;

    switch(staux[0]){

      // Comment.
    case '#': break;

      // Vertex.
    case 'v': 

      // Read the x, y and z values.
      sscanf(&staux[2], "%f %f %f", &vx, &vy, &vz);

      aux[0] = vx; aux[1] = vy; aux[2] = vz;
      // Append a new vertex.
      vertices.push_back(new Vertex3D(aux));

      break;

      // Face.
    case 'f': 

      sscanf(&staux[2], "%f %f %f", &a, &b, &c);
			
      Vertex3D  *t[3] = {vertices[--a], vertices[--b], vertices[--c]};
      // Store the vertex pointers.
      triangs.push_back (new Triangle(t));

      break;

    }// End switch.

  }// End while.
  
  fclose (f);
	
  //Object3D *o = new Object3D ();
  for (vector<Vertex3D *>::iterator it = vertices.begin (); it != vertices.end (); it++)
    this->add_vertex (*it);
  for (vector<Triangle *>::iterator it = triangs.begin (); it != triangs.end (); it++)
    this->add_triangle (*it);

  this->compute_com ();
  //return o;	
}


Object3D::~Object3D () 
{
}


void 
Object3D::draw ()
{
  //int n=0;
	glPushMatrix ();
	glTranslated (_translation[0], _translation[1], _translation[2]);
	glRotated (_rotation[0], 1.0, 0.0, 0.0);
	glRotated (_rotation[1], 0.0, 1.0, 0.0);
	glRotated (_rotation[2], 0.0, 0.0, 1.0);
	switch (get_state())
	{
		case NORMAL :
			_color[0] = _color[1] = _color[2] = 1.0;
			break;
		case SELECTED :
			_color[0] = 1.0;
			_color[1] = _color[2] = 0.0;
			break;
		case EDITED :
			_color[0] = _color[1] = 0.0;
			_color[2] = 1.0;
			break;
	}
	glMaterialfv(GL_FRONT, GL_DIFFUSE, _color);
	for (vector<Triangle *>::iterator it = triangs.begin (); it != triangs.end (); it++)
		{
		  //glLoadName(n);
		  //n++;
			glBegin (GL_TRIANGLES);
			for (int i = 0; i < 3; i++)
				{
					glNormal3dv ((*it)->vertex (i)-> normal ());
					glVertex3dv ((*it)->vertex (i)-> pos ());	
				}
			glEnd ();
		}
	glPopMatrix ();
	//glFlush ();
}

void 
Object3D::set_com (Vertex3D v)
{
	com = v;
}
		
void
Object3D::compute_com ()
{
	GLdouble v[3] = {0, 0, 0};
	for (vector<Vertex3D *>::iterator it = verts.begin (); it != verts.end (); it++)
		{
			v[0] += (*it)->x ();
			v[1] += (*it)->y ();
			v[2] += (*it)->z ();
		}
	for (int i = 0; i < 3; i++)
		{
			v[i] /= verts.size ();
			v[i] += _translation[i];
		}
	com = Vertex3D (v);
}

void
Object3D:: move (Vertex3D pos)
{
	Vertex3D com = get_com ();	
	GLdouble dist[3] = {pos.x () - com.x (), pos.y () - com.y (), pos.z () - com.z ()};
	for (int i = 0; i < 3; i++)
		_translation[i] += dist[i];																								
}
		
void 
Object3D :: scale (GLdouble factor, TAxis axis)
{
	/* Re-calculate center of mass */
	compute_com ();
	/* Vector from the center of mass to the point */
	GLdouble dist_p_com;
	Vertex3D com = get_com ();

	if (axis == X || axis == XY || axis == XZ || axis == XYZ)
		for (vector<Vertex3D *>::iterator it = verts.begin (); it != verts.end (); it++)
			{
				dist_p_com = ((*it)->x () - com.x ()) * factor;
				(*it)->set_x (com.x () + dist_p_com);
			}
	if (axis == Y || axis == YZ || axis == XY || axis == XYZ)
		for (vector<Vertex3D *>::iterator it = verts.begin (); it != verts.end (); it++)
			{
				dist_p_com = ((*it)->y () - com.y ()) * factor;
				(*it)->set_y (com.y () + dist_p_com);
			}
	if (axis == Z || axis == XZ || axis == YZ || axis == XYZ)
		for (vector<Vertex3D *>::iterator it = verts.begin (); it != verts.end (); it++)
			{
				dist_p_com = ((*it)->z () - com.z ()) * factor;
				(*it)->set_z (com.z () + dist_p_com);
			}
	for (vector<Vertex3D *>::iterator it = verts.begin (); it != verts.end (); it++)
		(*it)->compute_normal ();
}

void 
Object3D::rotate (GLdouble degree, TAxis axis)
{
	if (axis == X || axis == XY || axis == XZ || axis == XYZ)
		_rotation[0] += degree;
	if (axis == Y || axis == YZ || axis == XY || axis == XYZ)
		_rotation[1] += degree;
	if (axis == Z || axis == XZ || axis == YZ || axis == XYZ)
		_rotation[2] += degree;
}
		
void 
Object3D::extrude (Triangle *t, GLdouble dist)
{
	/* Candidates to be in the same face */
	vector<Triangle *> cand;
	GLdouble scalar_prod, mod1, mod2;
	for (vector<Triangle *>::iterator it = triangs.begin (); it != triangs.end (); it++)
		{	
			if ((*it) == t)
				continue;
			scalar_prod = (*it)->normalv ().x () * t->normalv ().x () + 
										(*it)->normalv ().y () * t->normalv ().y () + 
										(*it)->normalv ().z () * t->normalv ().z ();
			mod1 = sqrt (t->normalv ().x () * t->normalv ().x  () + 
									t->normalv ().y () * t->normalv ().y () +
									t->normalv ().z () * t->normalv ().z ());
			mod2 = sqrt ((*it)->normalv ().x () * (*it)->normalv ().x  () + 
									(*it)->normalv ().y () * (*it)->normalv ().y () +
									(*it)->normalv ().z () * (*it)->normalv ().z ());
			if (abs (scalar_prod) == abs (mod1 * mod2))
				cand.push_back (*it);
		}
	
	vector<Triangle *> face, face_temp, cand_temp;
	face.push_back(t);
	
	/* For each triangle, check if it's next to 
   * the triangles that compound the face */
	int total_added = 1;
	bool added = false;

	while (total_added > 0)
	{
		total_added = 0;
		//cand_temp = cand;
		for (vector<Triangle *>::iterator it = cand.begin (); it != cand.end (); it++)
		{	
			//face_temp = face;
			for(vector<Triangle *>::iterator f = face.begin (); f!=face.end () && added == false; f++)
			{
				if((*it)->adyacent(*f))
				{
					face.push_back(*it);
					total_added++;
					cand.erase(it);
					added = true;
				}
			}
		}	
	}

	Triangle *tr;
	for(vector<Triangle *>::iterator f = face.begin (); f != face.end (); f++)
	{
		/* Add the mirrowed triangles */
		for(int i=0;i<3;i++)
		{
			tr = new Triangle(*(*f));
			tr->vertex(i)->set_x (tr->vertex(i)->x () + ((tr->normalv ().x () * dist)));
			tr->vertex(i)->set_y (tr->vertex(i)->y () + ((tr->normalv ().y () * dist)));
			tr->vertex(i)->set_z (tr->vertex(i)->z () + ((tr->normalv ().z () * dist)));
			add_triangle(tr);
		}		
		/* Add the surrounding triangles */
		Vertex3D * t1[3];
		t1[0] = t->vertex (0);
		t1[1] = t->vertex (1);
		t1[2] = tr->vertex (1);
		add_triangle (new Triangle (t1));
		t1[0] = t->vertex (0);
		t1[1] = tr->vertex (0);
		t1[2] = tr->vertex (1);
		add_triangle (new Triangle (t1));
		t1[0] = t->vertex (0);
		t1[1] = tr->vertex (0);
		t1[2] = tr->vertex (2);
		add_triangle (new Triangle (t1));
		t1[0] = t->vertex (0);
		t1[1] = t->vertex (2);
		t1[2] = tr->vertex (2);
		add_triangle (new Triangle (t1));
		t1[0] = t->vertex (1);
		t1[1] = t->vertex (2);
		t1[2] = tr->vertex (1);
		add_triangle (new Triangle (t1));
		t1[0] = t->vertex (2);
		t1[1] = tr->vertex (1);
		t1[2] = tr->vertex (2);
		add_triangle (new Triangle (t1));
	
	}
		 

}


